/**
**Filename: QueueV1.01.c
**Authors: Maxwell Kobe, Jerry Causby, Ryan Gross
**Version: 1.8
**Last Modified: 12/8/2011
**/


#include <stdio.h>
#include <stdlib.h>
#include "mpx_supt.h"
#include <string.h>
#include "mrj_supt.h"
pcb *searchPriority(int);
void printReady();
pcb* returnHeadR();
pcb* returnHeadB();
void dequeueReadyMiddle(pcb*);
int free_pcb(pcb*);

pcb *pHeadR=NULL;
pcb *pTailR=NULL;
pcb *pHeadB=NULL;
pcb *pTailB=NULL;
int countb=0;  //blocked queue count
int countr=0;  //ready queue count

/*procedure: enqueueBlocked
**enqueues a pcb to the blocked queue
**arguments: pointer to pcb
**returns: nothing
**calls: none
*/

void enqueueBlocked(pcb* element)
{
	if(countb == 0)   //First element
	{
		pHeadB = element;   //first element is at the head of queue
		pTailB = element;   //first element is at the tail of queue
		element->next = NULL;
		element->prev = NULL;
		countb++;   
	}
	
	else
	{
		element->next = pTailB;   //set next to the tail element
		pTailB->prev = element;   //set last elements prev to new element
		element->prev = NULL;    //set previous to NULL
		pTailB = element;            //set tail pointer to the element
		countb++;
	}
}


/*procedure: dequeueBlocked
**dequeues head of blocked queue
**no arguments required 
**returns head pcb of blocked queue
**calls free_pcb
*/
pcb dequeueBlocked()
{
	pcb *pTemp = NULL;
	int x;
	if(countb == 0)
	{
		printf("The queue is empty.\n");
	}
	
	else
	{
		pTemp = pHeadB;               //Hold the value to be dequed in temp pointer
		pHeadB = pTemp->prev;     //Assign new head to previous pcb
		pHeadB->next = NULL;
		countb--;
		x = free_pcb(pTemp);
	}
	return *pTemp;             //Return head pcb
}

/*procedure: enqueueReady
**allocates memory for a new program control block
**arguments: pcb* of pcb to dequeue
**returns: nothing
**calls searchPriority
*/	
void enqueueReady(pcb* element)
{
	if(countr == 0)   //First element
	{
		pHeadR = element;   //first element is at the head of queue
		pTailR = element;   //first element is at the tail of queue
		countr++;
	}
	else
	{
		pcb *pBehind = (searchPriority(element->priority)); //search to find element priority
		if(pBehind == NULL)       //If element has lowest priority
		{
			element->next = pTailR;  //set element's next to tail's next
			pTailR->prev = element;  //set old tail's prev to new element
			element->prev = NULL;               //no previous node
			pTailR = element;  			//set tail pointer to the element
			countr++;
		}
		
		else if((pBehind == pHeadR))       //If element has highest priority
		{
			element->prev = pHeadR;    //set previous to the element behind the new element
			pHeadR->next = element;    //set pHeadR's next to the new element
			element->next = NULL;  //set next to NULL
			pHeadR = element;
			countr++;
		}
		else
		{
			element->next = pBehind->next;  //set next to behind's next
			pBehind->next->prev = element;
			element->prev = pBehind;    //set previous to the element behind the new element
			pBehind->next = element;    //set behind element's next to the new element
			countr++;
		}
	}
}

/*procedure: dequeueReady
**dequeues head of ready queue
**no arguments required 
**returns head pcb of ready queue
**calls free_pcb
*/
pcb* dequeueReady()
{
	pcb *pTemp = NULL;
	int x;
	if(countr == 0)
	{
		printf("The queue is empty.\n");
	}

	else
	{
		pTemp = pHeadR;               //Hold the value to be dequed in temp pointer
		pHeadR = pTemp->prev;     //Assign new head to previous pcb
		pHeadR->next = NULL;     //Assign new head's next to NULL
		countr--;
		x = free_pcb(pTemp);
	}

	return pTemp;             //Return head pcb
}
/*procedure: dequeueReadyMiddle
**removes a pcb from the ready queue
**arguments: pcb* of pcb to dequeue 
**returns head pcb of blocked queue
**calls dequeueReady
*/
void dequeueReadyMiddle(pcb* thePCB)
{
	if(countr == 0)
	{
		printf("The queue is empty.\n");
	}
	
	else if(thePCB == pHeadR)          //Element is head of queue
	{
		dequeueReady(thePCB);
	}
	
	else if(thePCB == pTailR)        //Element is tail of queue
	{
		thePCB->next->prev = NULL;
		pTailR = thePCB->next;
		countr--;
	}

	else                             //Element is actually in the middle somewhere
	{
		thePCB->prev->next = thePCB->next;
		thePCB->next->prev = thePCB->prev;
		countr--;
	}
}

/*procedure: searchPriority
**searches for the pcb closest to the supplied priority
**arguments: int priority
**returns pcb* to pcb that is the closest to the specified priority 
**calls none
**globals pHeadR
*/
pcb* searchPriority(int newPriority)
{
	pcb *pElementBehindNewElement;
	pcb *pTemp;
	pTemp = pHeadR;
	
	if(newPriority > (pHeadR->priority))   //Highest Priority
	{
		pElementBehindNewElement = pHeadR;
	}
	else if(newPriority < (pTailR->priority))  //Lowest priority
	{
		pElementBehindNewElement = NULL;
	}
	else
	{
		while(newPriority < (pTemp->priority))   //check if new element's priority is less than temp's
		{
			pTemp = pTemp->prev;  //Search backwards from head
		}

		pElementBehindNewElement = pTemp;
	}
	return pElementBehindNewElement;
}

/*procedure: searchPCBbyName
**locates the pcb* by the process's name
**arguments: char* name of pcb to find
**returns pcb* to pcb with the same name as the one provided in args. null if name not found
**calls: none
**globals: pHeadR, countr, countb, pHeadB
*/
pcb* searchPCBbyName(char* name)
{
	pcb* pTemp = pHeadR;   //Starts with seaching at the head of the ready queue
	int notHere = 0;     //Indicator as to whether or not the pcb is found
	
	while((strcmp(pTemp->name, name) != 0) && (notHere == 0)) //Search through ready queue
	{
		if(pTemp == pTailR) { notHere = 1;}  //At end of queue
		
		else if(countr == 0) { notHere = 1;} //Empty queue

		else
		{pTemp = pTemp->prev;}  //Not at end, keep looking
	}

	if(notHere == 1)   //Wasn't found in ready queue
	{
		notHere = 0;   //Reset notHere indicator
		pTemp = pHeadB;   //Set temp pointer to the blocked queue head
		while((strcmp(pTemp->name, name) != 0) && (notHere == 0)) //Search through blocked queue
		{
			if(pTemp == pTailB) { notHere = 1;}  //At end of queue
			
			else if(countb == 0) { notHere = 1;}  //Empty queue

			else
			{pTemp = pTemp->prev;}  //Not at end, keep looking
		}
	}

	if(notHere == 1)         //Wasn't found in either queue
	{
		return NULL;
	}
	return pTemp;
}
/*procedure: printReady
**prints all pcbs in the ready queue to terminal
**arguments: none
**returns nothing
**calls: none
**globals: pHeadR, countr
*/
void printReady()
{
	pcb* newPcb = NULL;
	int i=0;
	printf("-------------------------Key--------------------------------\n");
	printf("State: 	0 => run,  1 => ready,  2 => blocked\n");
	printf("Suspended?: 0 => NOT suspended,  1 => suspended\n");
	printf("Class: 	0 => system, 1 => app\n");
	printf("------------------------------------------------------------\n");
	printf("Name		Priority	State	Suspended?	Class	Mem_Size\n");
	
	newPcb = pHeadR;
	for(i=0; i<countr; i++)
	{
		printf("%s		   %d		  %d	   %d		%d	  %d\n", newPcb->name,newPcb->priority,newPcb->state,
				newPcb->susp,newPcb->Pclass,newPcb->memSize);
				
		newPcb = newPcb->prev;
	}
}

/*procedure: printBlocked
**prints all pcbs in the blocked queue to terminal
**arguments: none
**returns nothing
**calls: none
**globals: pHeadB, countb
*/
void printBlocked()
{
	pcb* newPcb = NULL;
	int i=0;
	printf("-------------------------Key--------------------------------\n");
	printf("State: 	0 => run,  1 => ready,  2 => blocked\n");
	printf("Suspended?: 0 => NOT suspended,  1 => suspended\n");
	printf("Class: 	0 => system, 1 => app\n");
	printf("------------------------------------------------------------\n");
	printf("Name		Priority	State	Suspended?	Class	Mem_Size\n");
	
	newPcb = pHeadB;
	for(i=0; i<countb; i++)
	{
		printf("%s		   %d		  %d	   %d		%d	  %d\n", newPcb->name,newPcb->priority,newPcb->state,
				newPcb->susp,newPcb->Pclass,newPcb->memSize);
				
		newPcb = newPcb->prev;
	}
}

//returns head of ready queue
pcb* returnHeadR()
{
	return pHeadR;
}

//returns head of blocked queue
pcb* returnHeadB()
{
	return pHeadB;
}